`include  "/home/zhoupu/ysyx-workbench/npc/vsrc/branch.v"

module top (
  input   clk,
  input   rst,
  output [63:0] pc_debug
);

  import "DPI-C" function void read_mem(input longint raddr, int len, output longint rdata);
  import "DPI-C" function void write_mem(input longint raddr, int len, longint wdata);


  wire [63:0] pc, pc_four, pc_branch, pc_next;
  wire [31:0] inst;
  wire [4:0] rs1, rs2, rd;
  wire RegWen;
  wire [63:0] imm, rdataA, rdataB, busA, busB, ALUout, MEMoutA, RegWdata;
  reg [63:0] MEMoutB;
  wire [1:0] rf_write_src;
  wire [2:0] ALUAsrc, ALUBsrc, branch;
  wire [3:0] ALUop, MDUop;

  always @(pc) begin
    read_mem(pc, 4, MEMoutA);
  end
  assign inst = MEMoutA[31:0];
  assign pc_debug = pc;

  wire zero, carry, negtive, overflow;
  wire [63:0] pc_offset, four_offset, beq_offset, bne_offset, bge_offset, blt_offset, bgeu_offset, bltu_offset;
  assign four_offset = 64'h0000_0000_0000_0004;
  assign beq_offset = zero ? imm : four_offset;
  assign bne_offset = ~zero ? imm : four_offset;
  assign bge_offset = ~(overflow ^ negtive) ? imm : four_offset;
  assign blt_offset = overflow ^ negtive ? imm : four_offset;
  assign bgeu_offset = carry ? imm : four_offset;
  assign bltu_offset = ~carry ? imm : four_offset;

  MuxKeyWithDefault #(8, 3, 64) branch_offset_mux (pc_offset, branch, four_offset, {
    `BRANCH_NO,   four_offset,
    `BRANCH_BEQ,  beq_offset,
    `BRANCH_BNE,  bne_offset,
    `BRANCH_BGE,  bge_offset,
    `BRANCH_BLT,  blt_offset,
    `BRANCH_BGEU, bgeu_offset,
    `BRANCH_BLTU, bltu_offset,
    `BRANCH_J,    four_offset
  });

  addr_64_bits pc_branch_addr(.a(pc),.b(pc_offset),.cin(1'b0),.out(pc_branch),.cout(),.P(),.G());
  addr_64_bits pc_four_addr(.a(pc),.b(four_offset),.cin(1'b0),.out(pc_four),.cout(),.P(),.G());

  MuxKeyWithDefault #(2, 3, 64) pc_mux (pc_next, branch, pc_branch, {
    `BRANCH_NO,  pc_four,
    `BRANCH_J,   ALUout
    //default    pc_branch
  });

  IFU ifu(
    .clk(clk),
    .rst(rst),
    .dnpc(pc_next),
    .pc(pc),
    .MemOp()
    //Memop
  );

  IDU idu(
    .clk(clk),
    .inst(inst),
    .rs1(rs1),
    .rs2(rs2),
    .rd(rd),
    .imm(imm),
    .ALUop(ALUop),
    .MDUop(MDUop),
    .ALUAsrc(ALUAsrc),
    .ALUBsrc(ALUBsrc),
    .Branch(branch),
    .RegWen(RegWen),
    .rf_write_src_out(rf_write_src),
    .MemRen(mem_ren),
    .MemWen(mem_wen),
    .MemOp(mem_op)
  );

  wire mem_wen, mem_ren;
  wire [2:0] mem_op;
  wire [63:0] mem_out;
  integer len;

  always @(*) begin
    case (mem_op[1:0])
      2'b00:  len = 1;
      2'b01:  len = 2;
      2'b10:  len = 4;
      2'b11:  len = 8;
      default: len = 4;
    endcase
  end

  always @(negedge clk) begin
    if (mem_ren) begin
      read_mem(ALUout, len, MEMoutB);
    end
  end

  always @(posedge clk) begin
    if(mem_wen) begin
      write_mem(ALUout, len, rdataB);
    end
  end

  MuxKey #(4, 2, 64) mem_out_sign_ext_selector(mem_out, mem_op[1:0], {
    2'b00, {{56{MEMoutB[7] & ~mem_op[2]}}, MEMoutB[7:0]},
    2'b01, {{48{MEMoutB[15] & ~mem_op[2]}}, MEMoutB[15:0]},
    2'b10, {{32{MEMoutB[31] & ~mem_op[2]}}, MEMoutB[31:0]},
    2'b11, MEMoutB
  });

  MuxKey #(3, 2, 64) rf_write_src_selector(RegWdata, rf_write_src, {
    2'b00, ALUout,
    2'b01, pc_four,
    2'b10, mem_out
  });

  RegisterFile rf(
    .clk(clk),
    .rst(rst),
    .wdata(RegWdata),
    .waddr(rd),
    .raddrA(rs1),
    .raddrB(rs2),
    .wen(RegWen),
    .rdataA(rdataA),
    .rdataB(rdataB)
  );

  MuxKey #(2, 3, 64) busA_mux ( busA, ALUAsrc, {
    3'b000, rdataA,
    3'b001, pc
  });

  MuxKey #(2, 3, 64) busB_mux ( busB, ALUBsrc, {
    3'b000, rdataB,
    3'b001, imm
  });

  ALU alu(
    .alu_op_in(ALUop),
    .mdu_op_in(MDUop),
    .a_in(busA),
    .b_in(busB),
    .alu_out(ALUout),
    .flag_out({negtive, zero, carry, overflow})
  );
      
endmodule  //name
